/*
   Filename: os.cpp
   Author: Abdelrahman Kamel, Takenori Tsuruga
   Date: April 27, 2009
   Discription: This program checks the suffix of the incoming
   perameter and makes sure it ends with ".s"; exits otherwise.
   It then creates 2 objects as, and vm and those two objects
   call assemble, and run. 
*/

/*modified for phase 2 undergoing */

#include "os.h"

OS::OS(){
	assemble_programs();	
}

void OS::assemble_programs(){

//system("ls *.s > programs 2>&-");
system("ls *.s >programs");
fstream fin;
fin.open("programs",ios::in);

string prog;

while(fin >> prog){
	as.assemble(prog);
	PCB * p = new PCB;
	p -> pName = prog;
	readyQ.push(p);
	pcb.push_back(p);
}

/*if(fin.fail()){
	cout << "No programs ending with suffix .s was found\n";
	system("rm programs");
	exit(1);
}*/

vm.loadmem(pcb);//load mem with pcb list
system("rm programs");
fin.close();
}

void OS::run(){

int idle_counter=0, idle_time=0;

//setting first job
running=readyQ.front();
readyQ.pop();

bool first_run = false;
int temp = 0;
while( true ){
   
	if(!first_run){
		temp = vm.clock-5;
		vm.run(running);
		running->CPU_time += (vm.clock-temp-5);
	}else{
		temp = vm.clock;
		vm.run(running);
		running->CPU_time += (vm.clock-temp);	
	}

   if( !waitingQ.empty() && (*waitingQ.front()).IO_clock <= vm.clock){
		readyQ.push(waitingQ.front());
		
		temp = vm.clock - (*waitingQ.front()).io_time;
		(*waitingQ.front()).io_time += temp;

		waitingQ.pop();
	}
  
  	int wait_stamp=0,ready_stamp=0; 
	switch(vm.get_vm_status()){

      case 0://Time slice occured
			readyQ.push(running);
			ready_stamp = vm.clock;
			break;
      case 1://I/O operation occured
         running->IO_clock = vm.clock + 26;
			waitingQ.push(running);         
			wait_stamp = vm.clock;
			break;
      case 2://Halt instr occured
         term_jobs.push_back(running);
			if(term_jobs.size() == pcb.size())
				goto done;
			break;
      case 3://out-bound
			(running->pcbfout) 
					<< "An out-of-bound reference was made." << endl << "PROGRAM TERMINATED!" << endl;
			term_jobs.push_back(running);
			break;
		case 4://stack overflow occured
			(running->pcbfout) << "Stack overflow occured." << endl << "PROGRAM TERMINATED!" << endl;
			term_jobs.push_back(running);
         break;
      case 5://Stack underflow occured
			(running->pcbfout) << "Stack underflow occured." << endl << "PROGRAM TERMINATED!" << endl;
			term_jobs.push_back(running);
         break;
      default:
         cout << "Invalid vm return status " << vm.get_vm_status() << endl;
         break;
   }//select
	
	//null because job came back and was placed in right queue
	running = 0;
	
	//idle case
	if(readyQ.empty() && running == 0)
	{
		for(;(*waitingQ.front()).IO_clock >= vm.clock; vm.clock++, idle_counter++);
		idle_time += idle_counter;
		idle_counter = 0;
		readyQ.push(waitingQ.front());
		temp = vm.clock - wait_stamp;
		(*waitingQ.front()).io_time += temp;

		waitingQ.pop();
	}

	if(!readyQ.empty()){
		running = readyQ.front();
		temp = vm.clock - ready_stamp;
		(*readyQ.front()).waiting_time += temp;
		readyQ.pop();
	}
	else running = 0;
	
	vm.clock += 5;//charging timer for context switch
	first_run = true;
}//while */
done:;

list<PCB *>::iterator itr;
itr = term_jobs.begin();

int throughPut=0;
//double asdf=0;
for(;itr != term_jobs.end(); itr++)
{
	asdf += (*itr)->CPU_time;
	(*itr)->pcbfout << "\nCPU Time: " << (*itr)->CPU_time << endl;
	(*itr)->pcbfout << "Largest stack size: " << (*itr)->largest_stack_size << endl;
	(*itr)->pcbfout << "I/O Time in waitingQ: " << (*itr)->io_time << endl;
	(*itr)->pcbfout << "Waiting Time in readyQ: " << (*itr)->waiting_time << endl;
	(*itr)->pcbfout << "Turn around time: " 
					<< static_cast<double>((*itr)->ta_time)/1000.0 << " second(s)\n";
	throughPut += (*itr)->ta_time;
}
cout << "\nCPU idled for: " << idle_time << " ticks.\n";
cout << "\nSystem Information: \n";
cout << "\tCPU Utilization: " << setprecision(4) 
	 << static_cast<double>((vm.clock-idle_time))/static_cast<double>(vm.clock)*100 << "%\n";
cout << "\tThroughput: " << throughPut/1000.0 << " second(s)\n\n";
//cout << asdf/static_cast<double>(vm.clock) << endl;
}

main(){

OS os;

os.run();

}//main

